#### PYTHONICA CORE PARSING MODULES ####

#Immediate TODO's:
#   1) Fix Exprnewton so it won't think we're trying to set something if someone
#      has an = sign.

#NOTES:
#   * If a function is nonchainable, you should have a check to make sure the
#     correct number of parameters are passed.

from __future__ import division
import string, math, random
from types import *
import pythonica, PiMath

gSymTable = {}
gInList = []
gOutList = []

def DataEval(data):
    # return an evaluated copy of the data
    return map(lambda d:d.Eval(), data)

def StoreInOut( indata, outdata ):
    global gInList, gOutList
    gInList.append(indata)
    gOutList.append(outdata)

#----------------------------------------------------------------------
#----------------------------------------------------------------------
# Functions represented by symbols (*, +, /, etc...) are of the form ExprHead(). (e.g. ExprTimes)
# Functions such as log(), sin(), etc. are of the form Exprhead(). (e.g. Exprtan)
class Expr:
    def __init__(self,data=None,head=None):
        if head: self.head = head
        self.setData(data)
        
    def __str__(self):
        return str(self.Head()) + '[' + \
            string.join(map(str,self.data), ',') + ']'
    
    def Head(self):
        try: return self.head
        except: return None
    
    def Name(self):
        try: return ExprSymbol(self.Head())
        except: return ExprSymbol(None)
    
    def __cmp__(self,other):
        if isinstance(other, Expr) and self.Head() == other.Head() and self.data == other.data:
            return 0
        else: return 1
        
    def Eval(self):
        return self
    
    def Simplify(self):
        return self
    
    def __repr__(self):
        return "<%s '%s'>" % (str(self.__class__), str(self))
    
    def setData(self, data):
        t = type(data)
        if not data: self.data = []
        elif t == TupleType: self.data = list(data)
        elif t == ListType: self.data = data
        else: self.data = [data]
        for i in range(len(self.data)):
            d = self.data[i]
            if type(d) == IntType or type(d) == FloatType or type(d) == ComplexType:
                self.data[i] = ExprNumber(d)
            elif not isinstance(d, Expr):
                raise "Expr::data", "Invalid data passed to Expr()!"

#----------------------------------------------------------------------
def ExprNumber(data=0):
    if type(data) == IntType or type(data) == FloatType:
        return ExprReal(data)
    elif type(data) == ComplexType:
        return ExprComplex(data)
    else:
        raise "ExprNumber::argx", "Invalid data passed to ExprNumber()"

#----------------------------------------------------------------------
class ExprReal(Expr):
    def __init__(self,data=0):
        self.setData(data)
        
    def __str__(self):
        if type(self.data) == IntType: return str(int(self.data))
        return str(self.data)
    
    def __int__(self):
        return int(self.data)
    
    def __float__(self):
        return float(self.data)

    def Head(self):
        return 'Real'
    
    def Name(self):
        if type(self.data) == IntType or int(data) == data:
            return ExprSymbol('Integer')
        else:
            return ExprSymbol('Real')
    
    def Eval(self):
        return self
    
    def setData(self,data):
        t = type(data)
        if not data: self.data = 0
        elif t == TupleType or t == ListType:
            if len(data) > 1:
                raise "Real::argx", "Real called with %i arguments; 0 or 1 arguments are expected." % len(data)
            elif len(data) == 1:
                if int(data) == data:
                    self.data = int(data[0])
                else:
                    self.data = float(data[0])
            else:
                self.data = 0
        elif t == FloatType and int(data) != data:
            self.data = float(data)
        else:
            self.data = int(data)

#----------------------------------------------------------------------
class ExprComplex(Expr):
    def __init__(self,data=None):
        self.setData(data)
    
    def __str__(self):
        return str(self.data)
    
    def __float__(self):
        return abs(self.data)
    
    def Head(self):
        return 'Complex'
    
    def Eval(self):
        return self
    
    def setData(self,data):
        t = type(data)
        if not data: self.data = complex(0,0)
        elif t == TupleType or t == ListType:
            if len(data) > 2:
                raise "Complex::argx", "Complex called with %i arguments; 0, 1 or 2 arguments are expected." % len(data)
            elif len(data) == 2:
                self.data = complex(data[0],data[1])
            elif len(data) == 1:
                self.data = complex(data[0])
            else:
                self.data = complex(0)
        else:
            self.data = complex(data)

#----------------------------------------------------------------------
class ExprSymbol(Expr):
    def __init__(self,data=''):
        self.setData(data)
        
    def __str__(self):
        return str(self.data)
    
    def Head(self):
        return 'Symbol'

    def Eval(self):
        global gSymTable
        if self.data in gSymTable.keys():
            # warning: this could cause recursion!
#            print self.data, "defined as", str(gSymTable[self.data]),
            out = gSymTable[self.data].Eval()
#           print "-->", repr(out)
            return out
        return self
    def setData(self,data):
        self.data = data

#----------------------------------------------------------------------

#global predefined symbols
#lowercase is the symbol, uppercase is the built-in True/False
##   I changed these to reflect the TI-83's way of interpreting equality
#true = ExprSymbol('True')
#false = ExprSymbol('False')
true = ExprReal('1')
false = ExprReal('0')

#----------------------------------------------------------------------
class ExprList(Expr):
    def Head(self):
        return 'List'
    
    def __str__(self):
        return '[' + string.join(map(str,self.data), ',') + ']'
    
    def Eval(self):
        data = DataEval(self.data)
        return ExprList(data)

#----------------------------------------------------------------------
class ExprSlice(Expr):
    def Head(self):
        return 'Slice'
    
    def __str__(self):
        return str(self.data[0]) + '[[' + string.join(map(str,self.data[1]), ',') + ']]'
    
    def Eval(self):
        if len(self.data) != 2:
            raise 'Slice::argx', 'Slice expects 2 or more arguments, %i arguments given.' % len(self.data)
        if type(self.data[1]) != ListType and type(self.data[1]) != TupleType:
            raise 'Slice::syntax', 'Syntax error in Slice definition.'
        if len(self.data[1]) == 0:
            raise 'Slice::argx', 'Slice expects 2 or more arguments, %i arguments given.' % (len(self.data[1]) + 1)
        expr = self.data[0].Eval()
        args = DataEval(self.data[1])
        for arg in args:
            if arg.Head() != 'Real' or int(arg.data) != arg.data:
                raise 'Slice::argt', 'Slice argument is not an integer.'
            if arg.data == 0:
                expr = expr.Name()
            elif (type(expr.data) != ListType and type(expr.data) != TupleType) or \
                    abs(arg.data) > len(expr.data):
                raise 'Splice::depth', 'Slice argument %i is longer than the depth of the expression.' % arg.data
            else:
                if arg.data > 0:
                    expr = expr.data[arg.data-1]
                else:
                    expr = expr.data[arg.data]
        return expr
    
    def setData(self, data):
        t = type(data)
        if not data: self.data = []
        elif t == TupleType: self.data = list(data)
        elif t == ListType: self.data = data
        else: self.data = [data]
        for i in range(len(self.data)):
            d = self.data[i]
            if type(d) == IntType or type(d) == FloatType or type(d) == ComplexType:
                self.data[i] = ExprNumber(d)
            elif not isinstance(d, (Expr, list)):
                raise "Expr::data", "Invalid data passed to Expr()!"

#----------------------------------------------------------------------
class ExprRule(Expr):
    def Head(self):
        return 'Rule'

#----------------------------------------------------------------------
class ExprReplaceAll(Expr):
    def Head(self):
        return 'ReplaceAll'
    
    def Eval(self):
        global gSymTable
        if len(self.data) != 2:
            raise "ReplaceAll::argx", "ReplaceAll called with %i arguments; 2 arguments are expected." % len(data)
        # to do the substitution, temporarily replace
        # the given symbol with the given value
        if self.data[1].__class__ != ExprRule:
            raise "ReplaceAll::argx", "ReplaceAll called with %s; ExprRule expected." % data[1].__class__
        symname = self.data[1].data[0].data
        if symname in gSymTable.keys(): oldval = gSymTable[symname]
        else: oldval = None
        gSymTable[symname] = self.data[1].data[1]
        out = self.data[0].Eval()
        if oldval: gSymTable[symname] = oldval
        else: del gSymTable[symname]
        return out
#----------------------------------------------------------------------
class ExprPlus(Expr):
    def Head(self):    return 'Plus'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) == 0: raise "Plus::argx","You can't have just a '+'!"
        num = 0
        extras = []
        for d in data:
            if d.Head() == 'Real' or d.Head() == 'Complex':
                num += d.data
            else:
                extras.append(d)
        if extras:
            # we partially reduced it...
            if num and type(num) == IntType or type(num) == FloatType or \
                    type(num) == ComplexType: extras.append(ExprNumber(num))
            elif len(extras) == 1:
                return extras[0].Eval()
            return ExprPlus(extras)
        else:
            # we completely reduced this to a Number
            return ExprNumber(num)

    def Simplify(self):
        # if any of our operands are the same, use a Times
        coeff = map(lambda x:1, self.data)
        data = []
        data[:] = self.data
        # convert expressions of the form '3x'
        for i in range(0,len(self.data)):
            data[i] = data[i].Simplify()
            if data[i].__class__ == ExprTimes and isinstance(data[i].data[0], ExprReal):
                coeff[i] = data[i].data[0].data
                if len(data[i].data) == 2:
                    data[i] = data[i].data[1]
                else:
                    data[i] = ExprTimes(data[i].data[1:])
        # now, combine terms additively
        for i in range(0,len(data)):
            if coeff[i]:
                for j in range(i+1,len(data)):
                    if data[i] == data[j]:
                        coeff[i] = coeff[i]+coeff[j]
                        coeff[j] = 0
        # now we have coefficients, make the new list
        repl = []
        for i in range(0,len(coeff)):
            if coeff[i] == 1:
                repl.append( data[i] )
            elif coeff[i]:
                repl.append( ExprTimes([ExprNumber(coeff[i]),data[i]]) )
        if len(repl) == 1:
            return repl[0]
        elif len(repl) == 0: return ExprNumber(0)
        else:
            return ExprPlus(repl)
        
#----------------------------------------------------------------------
class ExprTimes(Expr):
    def Head(self): return 'Times'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) < 2:
            raise "Times::argx", "Times called with %i arguments; 2 or more arguments are expected." % len(data)
        num = 1
        extras = []
        for d in data:
            if d.Head() == 'Real' or d.Head() == 'Complex':
                num = num * d.data
            else:
                extras.append(d)
        if extras and num != 0:
            # we partially reduced it...
            if num != 1: extras = [ExprNumber(num)] + extras
            elif len(extras) == 1:
                return extras[1].Eval()
            return ExprTimes(extras)
        else:
            # we completely reduced this to a Number
            return ExprNumber(num)

    def Simplify(self):
        data = []
        data[:] = self.data
        
        # first, try flattening -- if an argument is a Times,
        # absorb its arguments into our own
        for i in range(0,len(data)):
            if data[i].Head() == 'Times':
                data[i:i+1] = data[i].data
        # if any of our operands are the same, use a Power
        # build a list of terms, each stored as [coeff, base, power]
        terms = map(lambda x:[1,x.Simplify(),1], data)
        coeff = 1
        for i in range(0,len(terms)):
            # if it's a ExprNumber, collect separately
            if isinstance(terms[i][1], ExprReal):
                coeff = coeff * terms[i][1].data
                terms[i][0] = 0
            # if it's a ExprTimes, grab the coefficient
            if terms[i][1].__class__ == ExprTimes and \
                    isinstance(terms[i][1].data[0], ExprReal):
                terms[i][0] = terms[i][1].data[0].data
                if len(terms[i][1].data) == 2:
                    terms[i][1] = terms[i][1].data[1]
                else:
                    terms[i][1] = ExprTimes(terms[i][1].data[1:])
            # if it's an ExprPower, grab the power
            if terms[i][1].__class__ == ExprPower and \
                    isinstance(terms[i][1].data[1], ExprReal):
                terms[i][2] = terms[i][1].data[1].data
                terms[i][1] = terms[i][1].data[0]
        # now, combine terms additively
        for i in range(0,len(terms)):
            if terms[i][0]:
                for j in range(i+1,len(terms)):
                    if terms[i][1] == terms[j][1]:
                        terms[i][0] = terms[i][0] * terms[j][0]
                        terms[i][2] = terms[i][2] + terms[j][2]
                        terms[j][0] = 0
        # now we have powers, make the new list
        repl = []
        for i in range(0,len(terms)):
            if terms[i][2] != 0:
                if terms[i][2] != 1:
                    e = ExprPower( [terms[i][1], ExprNumber(terms[i][2])] )
                else: e = terms[i][1]
                if terms[i][0] == 1:
                    repl.append( e )
                elif terms[i][0]:
                    repl.append( ExprTimes([ExprNumber(terms[i][0]),e]) )
        # put the global coefficient on the front
        if coeff != 1:
            repl = [ExprNumber(coeff)] + repl
        if len(repl) == 1:
            return repl[0]
        elif not repl: return ExprNumber(1)
        return ExprTimes(repl)

#----------------------------------------------------------------------
class ExprMinus(Expr):
    # NOTE: this differs from Mathematica's Minus[] function,
    # which takes only one argument and returns its negative.
    # This takes one or two arguments, which makes parsing
    # much easier.
    def Head(self): return 'Minus'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) < 2:
            if data[0].Head() != 'Real' and data[0].Head() != 'Complex':
                return ExprTimes( [ExprNumber(-1),self.data[0]] )
            return ExprNumber(-data[0].data) #had to remove the self from the self.data[1].data
        elif len(data) == 2:
            if data[1].Head() == 'Real' or data[1].Head() == 'Complex':
                e = ExprNumber(-data[1].data) #had to remove the self from the self.data[1].data
            else: e = ExprTimes( [ExprNumber(-1),self.data[1]] )
            e2 = ExprPlus( [self.data[0],e] )
            return e2.Eval()
        else:
            raise "Minus::argx", "Minus called with %i arguments; 1 or 2 arguments are expected." % len(data)
        
#----------------------------------------------------------------------
class ExprDivide(Expr):
    def Head(self): return 'Divide'

    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 2:
            raise "Divide::argx", "Divide called with %i arguments; 2 arguments are expected." % len(data)
        if isinstance(data[0], (ExprReal, ExprComplex)) and \
                isinstance(data[1], (ExprReal, ExprComplex)):
            if isinstance(data[0], ExprReal):
                data[0].setData(float(data[0]))
            elif isinstance(data[1], ExprReal):
                data[1].setData(float(data[1]))
            return ExprNumber(data[0].data / data[1].data)
        else: return ExprDivide(data)

    def Simplify(self):
        # replace Divide with Power, where possible
        if self.data[1].Head() == 'Real':
            return ExprTimes( [ExprNumber(1.0/self.data[1].data),self.data[0]])
        e = ExprPower( [self.data[1],ExprNumber(-1)] )
        if self.data[0].Head() == 'Real' and self.data[0].data == 1:
            return e
        return ExprTimes( [self.data[0],e] )
        
#----------------------------------------------------------------------
class ExprSet(Expr):
    def Head(self): return 'Set'

    def Eval(self):
        global gSymTable
        if len(self.data) < 2:
            raise "Set::argx", "Set called with %i arguments; at least 2 arguments are expected." % len(data)
        data = [self.data[0]] + DataEval(self.data[1:])
        for d in data[:-1]:
            if d.Head() != "Symbol":
                raise "Set::ParamError", "First parameters of Set must be a Symbol, not %s" % d.Head()
            gSymTable[d.data] = data[-1]
        return data[-1]

#----------------------------------------------------------------------
class ExprUnset(Expr):
    def Head(self): return 'Unset'

    def Eval(self):
        global gSymTable
        if len(self.data) < 1:
            raise "Unset::argx", "Unset called with 0 arguments; at least 1 argument is expected."
        for d in self.data:
            if d.Head() != "Symbol":
                raise "Set::ParamError", "Parameters of Unset must be Symbol, not %s" % d.Head()
            if d.data in gSymTable.keys():
                del gSymTable[d.data]
        return ExprSymbol("OK")

#----------------------------------------------------------------------
class ExprPower(Expr):
    def Head(self): return 'Power'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 2:
#            print str(data[0]) + " :: " + str(data[1]) + " :: " + str(data[2])
            raise "Power::argx", "Power called with %i arguments; 2 arguments are expected." % len(data)
        if not isinstance(data[0], (ExprReal, ExprComplex)) or \
               not isinstance(data[1], (ExprReal, ExprComplex)):
            return ExprPower(data)
        return ExprNumber(data[0].data**data[1].data)

#----------------------------------------------------------------------
class ExprIn(Expr):
    def Head(self): return 'In'

    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "In::argx", "In called with %i arguments; 1 argument is expected." % len(data)
        if not isinstance(data[0], ExprReal):
            return self
        idx = int(data[0])
        # make positive numbers 1-indexed, to match Mathematica
        if idx > 0: idx -= 1
        try: return gInList[idx]
        except: return ExprIn(data)

#----------------------------------------------------------------------
class ExprOut(Expr):
    def Head(self): return 'Out'

    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "Out::argx", "Out called with %i arguments; 1 argument is expected." % len(data)
        if not isinstance(data[0], ExprReal):
            return self
        idx = int(data[0])
        # make positive numbers 1-indexed, to match Mathematica
        if idx > 0: idx -= 1
        try: return gOutList[idx].Eval()
        except: return self

#----------------------------------------------------------------------
class ExprEqual(Expr):
    def Head(self): return 'Equal'
    
    def Eval(self):
        data = DataEval(self.data)
        #No, no, no.  We should raise an exception if there's none or one arguments.
        #if len(self.data) < 2: return true
        if len(self.data) < 2:
            raise "Equal::argx", "Equal called with %i arguments; at least 2 are expected." % len(data)
#        if not isinstance(data[0], (ExprReal, ExprComplex)): return self
#        val = data[0].data
#        for d in data[1:]:
#            if not isinstance(d, (ExprReal, ExprComplex)): return self
#            if d.data != val: return false
#        return true
        #Use the defined __cmp__ on Eval
        ##I changed this because 3==4==0 would evaluate as zero instead of one.
        ##   I verified the answer should be 1 with the TI-83 just to be sure :-)
##        val = data[0]
##        for d in data[1:]:
##            if val != d: return false
        IsItEqual = false
        ValOne = data[0]
        for I in range(1, len(data), 1):
            ValTwo = data[I]
            if ValOne.Head() != 'Real' or ValTwo.Head() != 'Real': 
                    #Unfortunately this won't simplify. It would be nice if
                    #   2==2==3x+2x would evaluate to 1==5x.
                    #Also, I can't figure out how complex numbers work in pythonica,
                    #   so I'm not sure if the != 'Real' statement above is the 
                    #   best thing.  Should it include complex #s?
#                    print str(self) + " :: Self of Equal"
                    return self
            if float(ExprReal(ValOne)) != float(ExprReal(ValTwo)): IsItEqaul = false
            else: IsItEqual = true
            ValOne = IsItEqual
        return IsItEqual

#----------------BOOLEAN FUNCTIONS BY ME------------------------------
class ExprGreaterThan(Expr):
    def Head(self): return 'GreaterThan'
    
    def Eval(self):
        if len(self.data) < 2: 
            raise "GreaterThan::argx", "GreaterThan called with %i arguments; at least 2 arguments are expected" % len(data)
        data = DataEval(self.data)
        #Okay, I copied and pasted previous code, but you know what I mean when
        #   I say IsItEqual...
        IsItEqual = true
        ValOne = data[0]
        for I in range(1, len(data), 1):
            ValTwo = data[I]
            if ValOne.Head() != 'Real' or ValTwo.Head() != 'Real': 
                    return self
            if float(ExprReal(ValOne)) > float(ExprReal(ValTwo)): IsItEqual = true
            else: IsItEqual = false
            ValOne = IsItEqual
        return IsItEqual

#----------------------------------------------------------------------
class ExprLessThan(Expr):
    def Head(self): return 'LessThan'
    
    def Eval(self):
        if len(self.data) < 2:
            raise "LessThan::argx", "LessThan called with %i arguments; at least 2 arguments are expected." % len(data)
        data = DataEval(self.data)
        IsItEqual = true
        ValOne = data[0]
        for I in range(1, len(data), 1):
            ValTwo = data[I]
            if ValOne.Head() != 'Real' or ValTwo.Head() != 'Real': 
                    return self
            if float(ExprReal(ValOne)) < float(ExprReal(ValTwo)): IsItEqual = true
            else: IsItEqual = false
            ValOne = IsItEqual
        return IsItEqual

#----------------------------------------------------------------------
class ExprGreaterOrEqualTo(Expr):
    def Head(self): return 'GreaterOrEqualTo'
    
    def Eval(self):
        if len(self.data) < 2:
            raise "GreaterOrEqualTo::argx", "GreaterOrEqualTo called with %i arguments; at least 2 arguments are expected." % len(data)
        data = DataEval(self.data)
        IsItEqual = true
        ValOne = data[0]
        for I in range(1, len(data), 1):
            ValTwo = data[I]
            if ValOne.Head() != 'Real' or ValTwo.Head() != 'Real': 
                    return self
            if float(ExprReal(ValOne)) >= float(ExprReal(ValTwo)): IsItEqual = true
            else: IsItEqual = false
            ValOne = IsItEqual
        return IsItEqual

#----------------------------------------------------------------------
class ExprLessOrEqualTo(Expr):
    def Head(self): return 'LessOrEqualTo'
    
    def Eval(self):
        if len(self.data) < 2:
            raise "LessOrEqualTo::argx", "LessOrEqualTo called with %i arguments; at least 2 arguments are expected." % len(data)
        data = DataEval(self.data)
        IsItEqual = true
        ValOne = data[0]
        for I in range(1, len(data), 1):
            ValTwo = data[I]
            if ValOne.Head() != 'Real' or ValTwo.Head() != 'Real': 
                    return self
            if float(ExprReal(ValOne)) <= float(ExprReal(ValTwo)): IsItEqual = true
            else: IsItEqual = false
            ValOne = IsItEqual
        return IsItEqual

#----------------------------------------------------------------------
class ExprNotEqual(Expr):
    def Head(self): return 'NotEqual'
    
    def Eval(self):
        if len(self.data) < 2:
            raise "NotEqual::argx", "NotEqual called with %i arguments; at least 2 arguments are expected." % len(data)
        data = DataEval(self.data)
        IsItEqual = true
        ValOne = data[0]
        for I in range(1, len(data), 1):
            ValTwo = data[I]
            if ValOne.Head() != 'Real' or ValTwo.Head() != 'Real': 
                    return self
            if float(ExprReal(ValOne)) != float(ExprReal(ValTwo)): IsItEqual = true
            else: IsItEqual = false
            ValOne = IsItEqual
        return IsItEqual

#----------------------------------------------------------------------
class ExprAnd(Expr):
    def Head(self): return 'And'
    
    def Eval(self):
        if len(self.data) < 2:
            raise "And::argx", "And called with %i arguments; at least 2 arguments are expected." % len(data)
        data = DataEval(self.data)
        IsItEqual = true
        ValOne = data[0]
        for I in range(1, len(data), 1):
            ValTwo = data[I]
            if ValOne.Head() != 'Real' or ValTwo.Head() != 'Real': 
                    return self
            if float(ExprReal(ValOne)) != 0.0 and float(ExprReal(ValTwo)) != 0.0: IsItEqual = true
            else: IsItEqual = false
            ValOne = IsItEqual
        return IsItEqual

#----------------------------------------------------------------------
class ExprOr(Expr):
    def Head(self): return 'Or'
    
    def Eval(self):
        if len(self.data) < 2:
            raise "Or::argx", "Or called with %i arguments; at least 2 arguments are expected." % len(data)
        data = DataEval(self.data)
        IsItEqual = true
        ValOne = data[0]
        for I in range(1, len(data), 1):
            ValTwo = data[I]
            if ValOne.Head() != 'Real' or ValTwo.Head() != 'Real': 
                    return self
            if float(ExprReal(ValOne)) != 0.0 or float(ExprReal(ValTwo)) != 0.0: IsItEqual = true
            else: IsItEqual = false
            ValOne = IsItEqual
        return IsItEqual

#----------------------------------------------------------------------
class ExprXor(Expr):
    def Head(self): return 'Xor'
    
    def Eval(self):
        if len(self.data) < 2:
            raise "Xor::argx", "Xor called with %i arguments; at least 2 arguments are expected." % len(data)
        data = DataEval(self.data)
        IsItEqual = true
        ValOne = data[0]
        for I in range(1, len(data), 1):
            ValTwo = data[I]
            if ValOne.Head() != 'Real' or ValTwo.Head() != 'Real': 
                    return self
            if float(ExprReal(ValOne)) != 0.0 and float(ExprReal(ValTwo)) != 0.0 or \
               float(ExprReal(ValOne)) == 0.0 and float(ExprReal(ValTwo)) == 0.0: IsItEqual = true
            else: IsItEqual = false
            ValOne = IsItEqual
        return IsItEqual

#----------------------------------------------------------------------
#---------------------------------FUNCTIONS----------------------------
#----------------------------------------------------------------------
class Exprsin(Expr):

    def Head(self): return 'sin'
    #Will return my definition of sin because of the DegreeMode variable I have.
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "Sin::argx", "Sin called with %i arguments; 1 argument is expected." % len(data)
        if not isinstance(data[0], ExprReal):
            return Exprsin(data)
        return ExprNumber(PiMath.sin(data[0].data))

#----------------------------------------------------------------------
class Exprcos(Expr):
    
    def Head(self): return 'cos'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "Cos::argx", "Cos called with %i arguments; 1 argument is expected." % len(data)
        if not isinstance(data[0], ExprReal):
            return Exprcos(data)
        return ExprNumber(PiMath.cos(data[0].data))

#----------------------------------------------------------------------
class Exprtan(Expr):
    
    def Head(self): return 'tan'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "Tan::argx", "Tan called with %i arguments; 1 argument is expected." % len(data)
        if not isinstance(data[0], ExprReal):
            return Exprtan(data)
        return ExprNumber(PiMath.tan(data[0].data))

#----------------------------------------------------------------------
class Exprasin(Expr):
    
    def Head(self): return 'asin'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "ASin::argx", "ASin called with %i arguments; 1 argument is expected." % len(data)
        if not isinstance(data[0], ExprReal):
            return Exprasin(data)
        return ExprNumber(PiMath.asin(data[0].data))

#----------------------------------------------------------------------
class Expracos(Expr):
    
    def Head(self): return 'acos'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "ACos::argx", "ACos called with %i arguments; 1 argument is expected." % len(data)
        if not isinstance(data[0], ExprReal):
            return Expracos(data)
        return ExprNumber(PiMath.acos(data[0].data))

#----------------------------------------------------------------------
class Expratan(Expr):
    
    def Head(self): return 'atan'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "ATan::argx", "ATan called with %i arguments; 1 argument is expected." % len(data)
        if not isinstance(data[0], ExprReal):
            return Expratan(data)
        return ExprNumber(PiMath.atan(data[0].data))

#----------------------------------------------------------------------
class Exprradtodeg(Expr):
    
    def Head(self): return 'radtodeg'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "RadToDeg::argx", "RadToDeg called with %i arguments; 1 argument is expected." % len(data)
        if not isinstance(data[0], ExprReal):
            return Exprradtodeg(data)
        return ExprNumber(math.degrees(data[0].data))

#----------------------------------------------------------------------
class Exprdegtorad(Expr):
    
    def Head(self): return 'degtorad'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "DegToRad::argx", "DegToRad called with %i arguments; 1 argument is expected." % len(data)
        if not isinstance(data[0], ExprReal):
            return Exprdegtorad(data)
        return ExprNumber(math.radians(data[0].data))

#----------------------------------------------------------------------
class Exprsqrt(Expr):
    def Head(self): return 'sqrt'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "Sqrt::argx", "Sqrt called with %i arguments; 1 argument is expected." % len(data)
        if isinstance(data[0], ExprReal):
            return ExprNumber(math.sqrt(data[0].data))
        elif isinstance(data[0], ExprComplex):
            #I'm not sure why they do this if negative numbers cannot be raised to fractions
            return ExprNumber(pow(data[0].data, 0.5))
        else:
            return Exprsqrt(data)

#----------------------------------------------------------------------
#I changed the name to nroot from root.
class Exprnroot(Expr):
    def Head(self): return 'nroot'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 2:
            raise "nRoot::argx", "nRoot called with %i arguments; 2 arguments are expected." % len(data)
        if not isinstance(data[0], (ExprReal, ExprComplex)):
            return Exprnroot(data)
        num = data[0].data
        n = data[1].data
        #I added the following bit of code
        #   If the second argument is an odd integer, then we can take the root of a negative number
        if num < 0 and float(int((n+1)/2)) == float((n+1)/2):
            return ExprReal(-(abs(num)**(1.0/n)))
        return ExprReal(num**(1.0/n))

#----------------------------------------------------------------------
class Exprexp(Expr):
    def Head(self): return 'exp'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "Exp::argx", "Exp called with %i arguments; 1 argument is expected." % len(data)
        if not isinstance(data[0], ExprReal):
            return Exprexp(data)
        return ExprNumber(math.exp(data[0].data))

#----------------------------------------------------------------------
class Exprabs(Expr):
    def Head(self): return 'abs'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "Abs::argx", "Abs called with %i arguments; 1 argument is expected." % len(data)
        if not isinstance(data[0], (ExprReal, ExprComplex)):
            #This used to say ExprExp...was that right?
            return Exprabs(data)
        return ExprNumber(abs(data[0].data))

#----------------------------------------------------------------------
class Exprmod(Expr):
    def Head(self): return 'mod'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 2:
            raise "Mod::argx", "Mod called with %i arguments; 2 arguments are expected." % len(data)
        if not isinstance(data[0], ExprReal) or not isinstance(data[1], ExprReal):
            return Exprmod(data)
        return ExprNumber(data[0].data % data[1].data)

#----------------------------------------------------------------------
class Exprround(Expr):
    def Head(self): return 'round'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1 and len(data) != 2:
            raise "Round::argx", "Round called with %i arguments; 1 or 2 arguments are expected." % len(data)
        if len(data) == 2:
            n = data[1]
        else:
            n = ExprNumber(0)
        if not isinstance(data[0], ExprReal) or not isinstance(n, ExprReal):
            return Exprround(data)
        return ExprNumber(round(data[0].data, n.data))

#----------------------------------------------------------------------
class Exprrand(Expr):
    def Head(self): return 'rand'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 0:
            raise "Rand::argx", "Rand called with %i arguments; 0 arguments are expected." % len(data)
        return ExprNumber(random.random())

#----------------------------------------------------------------------
class Exprrandint(Expr):
    def Head(self): return 'randint'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1 and len(data) != 2:
            raise "RandInt::argx", "RandInt called with %i arguments; 1 or 2 arguments are expected." % len(data)
        if len(data) == 2:
            low = data[0]
            high = data[1]
        else:
            low = ExprNumber(0)
            high = data[0]
        if not isinstance(low, ExprReal) or not isinstance(high, ExprReal):
            return Exprrandint(data)
        if low.data >= high.data:
            raise "RandInt::range", "Invalid RandInt range"
        return ExprNumber(random.randint(low.data, high.data))

#----------------------------------------------------------------------
class Exprfloor(Expr):
    def Head(self): return 'floor'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "Floor::argx", "Floor called with %i arguments; 1 argument is expected." % len(data)
        if not isinstance(data[0], ExprReal):
            return Exprfloor(data)
        return ExprNumber(math.floor(data[0]))

#----------------------------------------------------------------------
class Exprceil(Expr):
    def Head(self): return 'ceil'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "Ceil::argx", "Ceil called with %i arguments; 1 argument is expected." % len(data)
        if not isinstance(data[0], ExprReal):
            return Exprceil(data)
        return ExprNumber(math.ceil(data[0]))

#----------------------------------------------------------------------
class Exprlog(Expr):
    def Head(self): return 'log'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1 and len(data) != 2:
            raise "Log::argx", "Log called with %i arguments; 1 or 2 arguments are expected." % len(data)
        if not isinstance(data[0], ExprReal) or \
                (len(data) == 2 and not isinstance(data[1], ExprReal)):
            return Exprlog(data)
        if len(data) == 2:
            return ExprNumber(math.log(data[0], data[1]))
        else:  #I edited this
            return ExprNumber(math.log10(data[0]))

#----------------------------------------------------------------------
#If someone said log10(23), it would get interpereted by PiMath as log10*(23),
#   so we can't use this.  I didn't like it anyway :-P
##class Exprlog10(Expr):
##    def Head(self): return 'log10'
##    
##    def Eval(self):
##        data = DataEval(self.data)
##        if len(data) != 1:
##            raise "Log10::argx", "Log10 called with %i arguments; 1 argument is expected." % len(data)
##        if not isinstance(data[0], ExprReal):
##            return Exprlog10(data)
##        return ExprNumber(math.log10(data[0]))

#----------------------------------------------------------------------
#------------------------DEFINED BY ME---------------------------------
#----------------------------------------------------------------------
class Exprln(Expr):
    def Head(self): return 'ln'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "Ln::argx", "Ln called with %i arguments; 1 argument is expected." % len(data)
        if not isinstance(data[0], ExprReal):
            return Exprln(data)
        return ExprNumber(math.log(data[0]))

#----------------------------------------------------------------------
class Exprcsc(Expr):
    def Head(self): return 'csc'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "Csc::argx", "Csc called with %i arguments; 1 argument is expected." % len(data)
        if not isinstance(data[0], ExprReal):
            return Exprcsc(data)
        return ExprNumber(PiMath.csc(data[0]))

#----------------------------------------------------------------------
class Exprsec(Expr):
    def Head(self): return 'sec'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "Sec::argx", "Sec called with %i arguments; 1 argument is expected." % len(data)
        if not isinstance(data[0], ExprReal):
            return Exprsec(data)
        return ExprNumber(PiMath.sec(data[0]))

#----------------------------------------------------------------------
class Exprcot(Expr):
    def Head(self): return 'cot'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "Cot::argx", "Cot called with %i arguments; 1 argument is expected." % len(data)
        if not isinstance(data[0], ExprReal):
            return Exprcot(data)
        return ExprNumber(PiMath.cot(data[0]))

#----------------------------------------------------------------------
class Exprsinh(Expr):
    def Head(self): return 'sinh'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "Sinh::argx", "Sinh called with %i arguments; 1 argument is expected." % len(data)
        if not isinstance(data[0], ExprReal):
            return Exprsinh(data)
        return ExprNumber(PiMath.sinh(data[0]))

#----------------------------------------------------------------------
class Exprcosh(Expr):
    def Head(self): return 'cosh'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "Cosh::argx", "Cosh called with %i arguments; 1 argument is expected." % len(data)
        if not isinstance(data[0], ExprReal):
            return Exprcosh(data)
        return ExprNumber(PiMath.cosh(data[0]))

#----------------------------------------------------------------------
class Exprtanh(Expr):
    def Head(self): return 'tanh'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "Tanh::argx", "Tanh called with %i arguments; 1 argument is expected." % len(data)
        if not isinstance(data[0], ExprReal):
            return Exprtanh(data)
        return ExprNumber(PiMath.tanh(data[0]))

#----------------------------------------------------------------------
class Exprfactorial(Expr):
    def Head(self): return 'factorial'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 1:
            raise "Factorial::argx", "Factorial called with %i arguments; 1 argument is expected." % len(data)
        if not isinstance(data[0], ExprReal):
            return Exprfactorial(data)
        return ExprNumber(PiMath.factorial(data[0]))

#----------------------------------------------------------------------
class Exprnpr(Expr):
    def Head(self): return 'npr'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 2:
            raise "nPr::argx", "nPr called with %i arguments; 2 arguments are expected." % len(data)
        if not isinstance(data[0], ExprReal) or not isinstance(data[1], ExprReal):
            return Exprnpr(data)
        return ExprNumber(PiMath.npr(data[0].data, data[1].data))

#----------------------------------------------------------------------
class Exprncr(Expr):
    def Head(self): return 'ncr'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 2:
            raise "nCr::argx", "nCr called with %i arguments; 2 arguments are expected." % len(data)
        if not isinstance(data[0], ExprReal) or not isinstance(data[1], ExprReal):
            return Exprncr(data)
        return ExprNumber(PiMath.ncr(data[0].data, data[1].data))

#----------------------------------------------------------------------
class Exprfofx(Expr):
    def Head(self): return 'fofx'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 3:
            raise "FOfX::argx", "FOfX called with %i arguments; 3 arguments expected." % len(data)
        #I'm not sure what these lines do, and they get in the way, so I don't think I need them.
#        if not isinstance(data[0], ExprReal) or not isinstance(data[1], ExprReal):
#             return Exprfofx(data)
        data[0] = pythonica.unparse(data[0])
        return ExprNumber(PiMath.fofx(str(data[0]), str(data[1].data), data[2].data))

#----------------------------------------------------------------------
class Exprsumseq(Expr):
    def Head(self): return 'sumseq'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 4 and len(data) != 5:
            raise "SumSeq::argx", "SumSeq called with %i arguments; 4 or 5 arguments expected." % len(data)
        data[0].data = pythonica.unparse(data[0])
        if len(data) == 4:
            return ExprNumber(PiMath.sumseq(str(data[0].data), str(data[1].data), data[2].data, data[3].data))
        else:
            return ExprNumber(PiMath.sumseq(str(data[0].data), str(data[1].data), data[2].data, data[3].data, data[4].data))

#----------------------------------------------------------------------
class Exprdefint(Expr):
    def Head(self): return 'defint'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 4 and len(data) !=5:
            raise "DefInt::argx", "DefInt called with %i arguments; 4 or 5 arguments expected." % len(data)
        data[0].data = pythonica.unparse(data[0])
        if len(data) == 3:
            return ExprNumber(PiMath.defint(str(data[0].data), str(data[1].data), data[2].data,))
        else:
            return ExprNumber(PiMath.defint(str(data[0].data), str(data[1].data), data[2].data, data[3].data))

#----------------------------------------------------------------------
class Exprdefderiv(Expr):
    def Head(self): return 'defderiv'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 3 and len(data) !=4:
            raise "DefDeriv::argx", "DefDeriv called with %i arguments; 3 or 4 arguments expected." % len(data)
        data[0].data = pythonica.unparse(data[0])
        if len(data) == 3:
            return ExprNumber(PiMath.defderiv(str(data[0].data), str(data[1].data), data[2].data,))
        else:
            return ExprNumber(PiMath.defderiv(str(data[0].data), str(data[1].data), data[2].data, data[3].data))

#----------------------------------------------------------------------
class Exprnewton(Expr):
    def Head(self): return 'newton'
    
    def Eval(self):
        data = DataEval(self.data)
        if len(data) != 3 and len(data) !=4:
            raise "Newton::argx", "Newton called with %i arguments; 3 or 4 arguments expected." % len(data)
        data[0].data = pythonica.unparse(data[0])
        if len(data) == 3:
            return ExprNumber(PiMath.newton(str(data[0].data), str(data[1].data), data[2].data,))
        else:
            return ExprNumber(PiMath.newton(str(data[0].data), str(data[1].data), data[2].data, data[3].data))

#----------------------------------------------------------------------
#--------------------define built-in constants-------------------------
#----------------------------------------------------------------------
ExprSet([ExprSymbol('pi'),ExprNumber(math.pi)]).Eval()
ExprSet([ExprSymbol('e'),ExprNumber(math.e)]).Eval()

#----------------------------------------------------------------------
# end of PythonicaCore.py
#----------------------------------------------------------------------
